<?php

declare(strict_types=1);
/**
 * 创建人 : Forpastime
 *
 * @link     http://hypercmf.com
 * @document https://hyperf.wiki
 * @contact  forpastime@qq.com
 * @git  https://gitee.com/hypercmf/hypercmf1.0.git
 */

use App\Model\SystemSetting;
use Hyperf\Utils\ApplicationContext;

/**
 * 将驼峰法转换为下划线
 * @param $string
 * @return string
 */
function for_convert_to_underscore($string){

    $pattern = '/([a-z])([A-Z])/';

    $replacement = '${1}_${2}';

    return strtolower(preg_replace($pattern, $replacement, $string));

}
/**
 * @return false|string
 */
function for_date_format()
{
    return date('Y-m-d');
}

/**
 * 替代scan_dir的方法
 * @param string $pattern 检索模式 搜索模式 *.txt,*.doc; (同glog方法)
 * @param int    $flags
 * @param        $pattern
 * @return array
 */
function for_scan_dir($pattern, $flags = null)
{
    $files = glob($pattern, $flags);
    if (empty($files)) {
        $files = [];
    } else {
        $files = array_map('basename', $files);
    }

    return $files;
}

/**
 * 获取某个目录下所有子目录
 * @param $dir
 * @return array
 */
function for_sub_dirs($dir)
{
    $dir     = ltrim($dir, "/");
    $dirs    = [];
    $subDirs = for_scan_dir("$dir/*", GLOB_ONLYDIR);
    if (!empty($subDirs)) {
        foreach ($subDirs as $subDir) {
            $subDir = "$dir/$subDir";
            array_push($dirs, $subDir);
            $subDirSubDirs = for_sub_dirs($subDir);
            if (!empty($subDirSubDirs)) {
                $dirs = array_merge($dirs, $subDirSubDirs);
            }
        }
    }

    return $dirs;
}

/**
 * 异常抛出.
 * @param $reason 报错原因
 * @param $detail 详情描述
 * @return string
 */
function for_error_exception(Throwable $throwable)
{
    $debug = env('DEBUG', false);
    if ($debug == true) {
        var_dump(get_class($throwable), $throwable->getTraceAsString());
        return $throwable;
    }
    return 'Whoops ! There is sth wrong at ' . $throwable->getLine();
}

/*
 * 加载路由
 * @param routesPath route存放的文件夹目录
 * @param deep 路由文件存放深度，必须统一放在一级目录下或者都放在二级目录下
 */
if (! function_exists('for_load_routes')) {
    function for_load_routes($routesPath, $deep = 1)
    {
        $path = BASE_PATH . DIRECTORY_SEPARATOR . $routesPath;

        $dirs = scandir($path);
        if ($deep == 1) {
            foreach ($dirs as $file) {
                if ($file != '.' && $file != '..') {
                    $routeFilePath = $path . "/{$file}";
                    require_once $routeFilePath;
                }
            }
        } elseif ($deep == 2) {
            foreach ($dirs as $dir) {
                if ($dir != '.' && $dir != '..') {
                    $routeFilePath = $path . "/{$dir}";
                    $files = scandir($routeFilePath);
                    foreach ($files as $file) {
                        if ($file != '.' && $file != '..') {
                            require_once $routeFilePath . DIRECTORY_SEPARATOR . basename($file);
                        }
                    }
                }
            }
        }
    }
}

function for_url()
{
}

/**
 * 获取文件扩展名
 * @param string $filename 文件名
 * @return string 文件扩展名
 */
function for_get_file_extension($filename)
{
    $pathinfo = pathinfo($filename);
    return strtolower($pathinfo['extension']);
}
/**
 * 密码加密方法.
 * @param string $pw 要加密的原始密码
 * @param string $authCode 加密字符串
 * @return string
 */
function for_password($pw, $authCode = '')
{
    if (empty($authCode)) {
        $authCode = env('AUTH_SOLT');
    }
    return md5(md5($authCode . $pw));
}

/**
 * 随机字符串生成.
 * @param int $len 生成的字符串长度
 * @return string
 */
function for_random_string($len = 6)
{
    $chars = [
        'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k',
        'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
        'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
        'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
        'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '0', '1', '2',
        '3', '4', '5', '6', '7', '8', '9',
    ];
    $charsLen = count($chars) - 1;
    shuffle($chars);    // 将数组打乱
    $output = '';
    for ($i = 0; $i < $len; ++$i) {
        $output .= $chars[mt_rand(0, $charsLen)];
    }
    return $output;
}

/**
 * 格式化参数格式化成url参数
 */
function for_array_to_url_params($arr)
{
    $buff = "";
    foreach ($arr as $k => $v) {
        if ($k != "sign" && $v != "" && !is_array($v)) {
            $buff .= $k . "=" . $v . "&";
        }
    }

    $buff = trim($buff, "&");
    return $buff;
}

/**
 * 签名算法实现  便于后期扩展微信不同的加密方式
 * @param string $signStr
 * @return string
 * @throws GatewayException
 */
function for_make_sign($array,$signType,$key)
{
    //$array['appId'] = $this->config['app_id'];
    //将数组排序并用&拼接字符串
    ksort($array);
    $signStr = for_array_to_url_params($array);

    switch ($signType) {
        case 'MD5':
            $signStr .= '&key=' . $key;
            $sign = md5($signStr);
            break;
        case 'HMAC-SHA256':
            $signStr .= '&key=' .  $key;
            $sign = strtoupper(hash_hmac('sha256', $signStr,  $key));
            break;
        default:
            break;
    }

    return strtoupper($sign);
}

function for_array_to_xml($arr)
{
    $xml = "<xml>";
    foreach ($arr as $key => $val) {
        if (is_array($val)) {
            $xml .= "<" . $key . ">";
            foreach ($val as $ks => $vs) {
                if (is_numeric($val)) {
                    $xml .= "<" . $ks . ">" . $vs . "</" . $ks . ">";
                } else {
                    $xml .= "<" . $ks . "><![CDATA[" . $vs . "]]></" . $ks . ">";
                }
            }
            $xml .= "</" . $key . ">";
        } else if (is_numeric($val)) {
            $xml .= "<" . $key . ">" . $val . "</" . $key . ">";
        } else {
            $xml .= "<" . $key . "><![CDATA[" . $val . "]]></" . $key . ">";
        }
    }
    $xml .= "</xml>";
    return $xml;
}

function for_xml_to_json($source)
{
    if (is_file($source)) { //传的是文件，还是xml的string的判断
        $xml_array = simplexml_load_file($source);
    } else {
        $xml_array = simplexml_load_string($source, null, LIBXML_NOCDATA);
    }
    $json = json_encode($xml_array);
    // $json = json_decode($json,true); //php5，以及以上，如果是更早版本，请查看JSON.php
    return $json;
}

/**
 * 判断字符串是否为手机号
 * @param $str
 * @return bool
 */
function for_is_mobile_number($str){
    if(preg_match("/^1[3456789]\d{0}$/)",$str)){
        return true;
    }else{
        return false;
    }
}


/**
 * 生成一次性TOKEN
 * @return array
 */
function for_generate_user_token(){
    $currentTime    = time();
    $expireTime     = $currentTime + 24 * 3600 * 180;
    $token          = md5(uniqid()) . md5(uniqid());
    return compact("expireTime","token");
}
/**
 * 生成支付相关单号
 * @return string
 */
function for_out_trade_no(){
    $time = microtime(true)*1000;
    return $time.mt_rand(100,999);
}

/**
 * POST 请求方法
 * @param $url
 * @param $post_data
 * @return bool|string
 */
function for_http_post($url, $post_data,$ContenType='application/x-www-form-urlencoded')
{
    $headerArray = array(
        'Accept:application/json, text/javascript, */*',
        'Content-Type:'.$ContenType,
        'Referer:https://mp.weixin.qq.com/'
    );

    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    // 对认证证书来源的检查，0表示阻止对证书的合法性的检查。
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    // 从证书中检查SSL加密算法是否存在
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);//关闭直接输出
    curl_setopt($ch, CURLOPT_POST, 1);//使用post提交数据
    curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);//设置 post提交的数据
    curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.69 Safari/537.36');//设置用户代理
    curl_setopt($ch, CURLOPT_HTTPHEADER, $headerArray);//设置头信息


    $res = curl_exec($ch);

    curl_close($ch);

    return $res;


}

/**
 * error code 说明.
 * <ul>
 *    <li>-40001: 签名验证错误</li>
 *    <li>-40002: xml解析失败</li>
 *    <li>-40003: sha加密生成签名失败</li>
 *    <li>-40004: encodingAesKey 非法</li>
 *    <li>-40005: appid 校验错误</li>
 *    <li>-40006: aes 加密失败</li>
 *    <li>-40007: aes 解密失败</li>
 *    <li>-40008: 解密后得到的buffer非法</li>
 *    <li>-40009: base64加密失败</li>
 *    <li>-40010: base64解密失败</li>
 *    <li>-40011: 生成xml失败</li>
 * </ul>
 */
$OK = 0;
$ValidateSignatureError = -40001;
$ParseXmlError = -40002;
$ComputeSignatureError = -40003;
$IllegalAesKey = -40004;
$ValidateAppidError = -40005;
$EncryptAESError = -40006;
$DecryptAESError = -40007;
$IllegalBuffer = -40008;
$EncodeBase64Error = -40009;
$DecodeBase64Error = -40010;
$GenReturnXmlError = -40011;
/**
 * 用SHA1算法生成安全签名
 * @param string $token 票据
 * @param string $timestamp 时间戳
 * @param string $nonce 随机字符串
 * @param string $encrypt 密文消息
 */
function for_get_sha1($token, $timestamp, $nonce, $encrypt_msg)
{
    //排序
    try {
        $array = array($encrypt_msg, $token, $timestamp, $nonce);
        sort($array, SORT_STRING);
        $str = implode($array);
        return array(1, sha1($str));
    } catch (Exception $e) {
        //print $e . "\n";
        return array(-40003, null);
    }
}


/**
 * 子孙树 用于菜单整理
 * @param $param
 * @param int $pid
 */
function for_sub_tree($param, $pid = 0)
{
    static $res = [];
    foreach($param as $key=>$vo){

        if( $pid == $vo['pid'] ){
            $res[] = $vo;
            for_sub_tree($param, $vo['id']);
        }
    }

    return $res;
}

/**
 * 整理菜单住方法
 * @param $param
 * @return array
 */
function for_prepare_menu($param)
{
    $param = for_obj_to_array($param);
    $parent = []; //父类
    $child = [];  //子类

    foreach($param as $key=>$vo){

        if(0 == $vo['parent_id']){
            $vo['href'] = '#';
            $parent[] = $vo;
        }else{
            $vo['href'] = for_convert_to_underscore($vo['control_name'] .'/'. $vo['action_name']); //跳转地址
            $child[] = $vo;
        }
    }
    foreach($parent as $key=>$vo){
        foreach($child as $k=>$v){
            if($v['parent_id'] == $vo['id']){
                $parent[$key]['children'][] = $v;
            }
        }
    }
    unset($child);

    return $parent;
}

/**
 * 解析备份sql文件
 * @param $file
 */
function for_back_up_database($file)
{
    // sql文件包含的sql语句数组
    $sqls = array ();
    $f = fopen ( $file, "rb" );
    // 创建表缓冲变量
    $create = '';
    while ( ! feof ( $f ) ) {
        // 读取每一行sql
        $line = fgets ( $f );
        // 如果包含空白行，则跳过
        if (trim ( $line ) == '') {
            continue;
        }
        // 如果结尾包含';'(即为一个完整的sql语句，这里是插入语句)，并且不包含'ENGINE='(即创建表的最后一句)，
        if (! preg_match ( '/;/', $line, $match ) || preg_match ( '/ENGINE=/', $line, $match )) {
            // 将本次sql语句与创建表sql连接存起来
            $create .= $line;
            // 如果包含了创建表的最后一句
            if (preg_match ( '/ENGINE=/', $create, $match )) {
                // 则将其合并到sql数组
                $sqls [] = $create;
                // 清空当前，准备下一个表的创建
                $create = '';
            }
            // 跳过本次
            continue;
        }

        $sqls [] = $line;
    }
    fclose ( $f );

    return $sqls;
}


/**
 * 对象转换成数组
 * @param $obj
 */
function for_obj_to_array($obj)
{
    return json_decode(json_encode($obj), true);
}

/**
 * 权限检测
 * @param $rule
 */
function for_auth_check($rule)
{
    $control = explode('/', $rule)['0'];
    if(in_array($control, ['login', 'index'])){
        return true;
    }

    if(in_array($rule, cache(session('role_id')))){
        return true;
    }

    return false;
}

/**
 * 获取Redis连接池
 */
function for_redis_pool(){
    $redis = ApplicationContext::getContainer()->get(Redis::class);
    return $redis;
}


/**
 * 读取存储配置
 * @return mixed
 * @throws \Psr\Container\ContainerExceptionInterface
 * @throws \Psr\Container\NotFoundExceptionInterface
 */
function for_storage_config(){
    $redis = for_redis_pool();
    if($storageConfig = $redis->get('storage_setting')){
        $storage = json_decode($storageConfig,true);
        $storage = json_decode($storage['value'],true);
    }else{
        $storageConfig = SystemSetting::where('key','storage')->first();
        if(!$storageConfig){
            return Result::error('获取上传配置失败');
        }else{
            $redis->set('storage_setting',json_encode($storageConfig->toArray()));
        }
        $storage = $storageConfig->toArray();
        $storage = json_decode($storage['value'],true);
    }
    return $storage;
}

/**
 * 整理出tree数据 ---  layui tree
 * @param $pInfo
 * @param $spread
 * @param $suffix 是否显示后缀A表示为注解生成节点，M表示后台添加菜单节点
 */
function for_get_tree($pInfo, $spread = true, $suffix=false)
{

    $res = [];
    $tree = [];
    if($suffix){
        $actionFix = 'A';
        $menuFix = '';
    }else{
        $actionFix = '';
        $menuFix = '';
    }
    //整理数组
    foreach($pInfo as $key=>$vo){

        if($spread){
            $vo['spread'] = true;  //默认展开
            $vo['title'] = $vo['type']==1?$vo['title']." $menuFix":$vo['title']." $actionFix";
        }
        $res[$vo['id']] = $vo;
        $res[$vo['id']]['children'] = [];
    }
    unset($pInfo);

    //查找子孙
    foreach($res as $key=>$vo){
        if(0 != $vo['pid']){
            if($spread){
                $res[$vo['pid']]['spread'] = true;  //默认展开
            }
            $res[$vo['pid']]['children'][] = &$res[$key];
        }
    }

    //过滤杂质
    foreach( $res as $key=>$vo ){
        if(0 == $vo['pid']){
            $tree[] = $vo;
        }
    }
    unset( $res );

    return $tree;
}
function for_get_host($headers){
    if(strstr($headers['referer']['0'],'https')===false){
        $protocol = 'http://';
    }else{
        $protocol = 'https://';
    }
    return $protocol.$headers['host'][0];
}
function for_paginator_data_formater($res){
    $data['total'] = $res->total();
    $data['current_page'] = $res->currentPage();
    $data['last_page'] = $res->lastPage();
    $data['per_page'] = $res->perPage();
    $data['has_more'] = $res->hasMorePages();
//    $data['data'] = $res->items();
    foreach ($res->items() as $k=>$v){
        $data['data'][$k] = is_array($v)?$v:$v->toArray();
    }
    return $data;
}

/**
 * 对象存储 图片样式处理
 * @param $url
 * @return string
 * @throws \Psr\Container\ContainerExceptionInterface
 * @throws \Psr\Container\NotFoundExceptionInterface
 */
function for_image_url_style($data){
    if($data['media_type']==1 && strstr($data['url'],'http') !==false){
        switch ($data['store_type']){
            case 2:
                $style = env("OSS_STYLE");
                break;
            case 3:
                $style = env("QINIU_STYLE");
                break;
            default:
                $style = "";
                break;
        }
        return $data['url'].$style;
    }else{
        return $data['url'];
    }
}
/**
 * 数据字段字符串转换为数组
 * @param $field
 * @return array|false|string[]
 */
function for_field_str_to_array($field){
    if($field!='*' && !is_array($field)){
        $field = explode(',',$field);
    }
    return $field;
}

/**
 * 切分SQL文件成多个可以单独执行的sql语句
 * @param        $file            string sql文件路径
 * @param        $tablePre        string 表前缀
 * @param string $charset         字符集
 * @param string $defaultTablePre 默认表前缀
 * @param string $defaultCharset  默认字符集
 * @return array
 */
function for_split_sql($file, $tablePre, $charset = 'utf8mb4', $defaultTablePre = 'for_', $defaultCharset = 'utf8mb4')
{
    if (file_exists($file)) {
        //读取SQL文件
        $sql = file_get_contents($file);
        $sql = str_replace("\r", "\n", $sql);
        $sql = str_replace("BEGIN;\n", '', $sql);//兼容 navicat 导出的 insert 语句
        $sql = str_replace("COMMIT;\n", '', $sql);//兼容 navicat 导出的 insert 语句
        $sql = str_replace($defaultCharset, $charset, $sql);
        $sql = trim($sql);
        //替换表前缀
        $sql  = str_replace(" `{$defaultTablePre}", " `{$tablePre}", $sql);
        $sqls = explode(";\n", $sql);
        return $sqls;
    }

    return [];
}